此時,我們希望可以透過query的方式將db裡的用戶都顯示出來。
因此今天將著重存在db的資料結構調整。
現在db裡的結構如下
但是想要做到搜尋,我們沒辦法使用no-sql的資料結構去一個個找key。因此在此我們要修改一下資料結構。
思考一下對話的資料結構可能的樣貌,由於我們想要保留每個已存在的對話可以被顯示在conversation table view當中,因此每個使用者都會有自己的user array。
/*
[
[
"name": ,
"safe_email
],
[
"name": ,
"safe_email
],
]
*/
當我們新增(註冊)一個使用者時,在insertUser
新增以下的內容
我們檢查當下是否有以這個帳號為key的array
self.ref.child("users").observeSingleEvent(of: .value, with: { snapshot in
有的話就append、沒有的話就新增。
if var usersCollection = snapshot.value as? [[String: String]] {
// append to user dictionary
let newElement = [
"name": user.firstName + " " + user.lastName,
"email": user.safeEmail
]
usersCollection.append(newElement)
self.ref.child("users").setValue(usersCollection, withCompletionBlock: { error , _ in
guard error == nil else {
completion(false)
return
}
completion(true)
})
} else {
let newCollection: [[String: String]] = [
[ "name": user.firstName + " " + user.lastName,
"email": user.safeEmail
]
]
self.ref.child("users").setValue(newCollection, withCompletionBlock: { error , _ in
guard error == nil else {
completion(false)
return
}
completion(true)
})
}
})
completion(true)
然後我們接下來想要做的是,如果現在手邊有all user就直接filter,沒有的話就先fetch再filter。
因此我們先宣告會使用到的東西。
在此result
是filter後的結果,而hasFetched
則是一個開關。
private let spinner = JGProgressHUD(style: .dark)
private var users = [[String: String]]()
private var results = [[String: String]]()
private var hasFetched = false
接著就是將剛剛提到的邏輯實作出來,我們使用replacingOccurrences
確保query的內容已經被trim過。
extension NewConversationViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let text = searchBar.text, !text.replacingOccurrences(of: " ", with: "").isEmpty else {
return
}
results.removeAll()
spinner.show(in: view)
self.searchUsers(query: text)
}
拿到處理好的query後,開始從db比對。
func searchUsers(query: String) {
// check if array has firebase result
if hasFetched {
self?.filterUsers(with: query)
} else {
// fetch then filter
DatabaseManager.shared.getAllUsers(completion: { [weak self] result in
switch result {
case .success(let usersCollection):
self?.users = usersCollection
self?.filterUsers(with: query)
case .failure(let error):
print("Fail to get user: \(error)")
}
})
}
}
func filterUsers(with term: String){
// update the UI
guard hasFetched else {
return
}
let results: [[String: String]] = self.users.filter {
guard let name = $0["name"]?.lowercased() else{
return false
}
return name.hasPrefix(term.lowercased())
}
self.results = results
updateUI()
}
最後將結果顯示出來,如果有結果就將tableView刷新顯示;沒有的話就顯示noResult
的label。而這兩的內容都已經有再先前寫好了!
func updateUI() {
if results.isEmpty {
self.noResultLabel.isHidden = false
self.tableView.isHidden = true
} else {
self.noResultLabel.isHidden = true
self.tableView.isHidden = false
self.tableView.reloadData()
}
}
}
最後我們在Load進來的時候把subview都加進來
view.addSubview(noResultLabel)
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
當然也別忘了跟隨著tableView.delegate
以及tableView.dataSource
的extension。
extension NewConversationViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = results[indexPath.row]["name"]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// start conversation
}
}
留了一個小伏筆,在didSelectRowAt
方法中,也就是當點選某個row,即代表開始展開對話~
終於要開始實作這30天的重點了嗎(??)
若上述內容有誤或可以改進的部分,歡迎留言以及提出任何指教~
謝謝 (´・∀・`)